ci: wire typecheck/test/astro-check/gh-pages-build into CI#1
Merged
Conversation
…ct tests ViewerRoute is a discriminated union — accessing objectId/noteId requires narrowing on view === "exhibit" first. The W3CAnnotation → Record cast needs an unknown intermediate since the extension key isn't part of the nominal type.
…erLike and OSD Viewer @types/openseadragon declares OverlayOptions.element as HTMLElement-only and location as Point|Rect, but frame-overlay.ts deliberately keeps FrameViewerLike duck-typed and decoupled from OSD's concrete types (so it stays test-mockable without importing OSD). Narrow the element type to match OSD's real contract, cast the SVG element at its one call site (SVG works fine at runtime, OSD's types just don't say so), and assert Viewer satisfies FrameViewerLike once at the mount.ts wiring boundary instead of coupling the interface back to OSD.
…tyTypes test errors Array/NodeList indexing and open-handler lookups return T|undefined under noUncheckedIndexedAccess; assert non-null with ! at the known-populated indices (same idiom as existing render-core tests). The innerHTML spy's PropertyDescriptor.get needs a conditional spread instead of `get: desc?.get` since exactOptionalPropertyTypes rejects an explicit undefined on an optional property. render-mount typecheck (`pnpm --filter @render/mount exec tsc --noEmit`) is now fully green; all 116 tests still pass.
… interfaces store.ts already re-exported these as type aliases of @render/core's WorkingObjectMeta/WorkingExhibitMeta/WorkingLibraryMeta (the Q-3 archie-persistence migration), but the old local interface declarations were never deleted — same field lists, same shapes, just stale. TS reported them as conflicting exported declarations, and their remaining RightsFields/MediaType/Section/Reading references as unresolvable (those imports were dropped when the migration landed). Re-import the Working* types under local aliases so both the file's own usages and the re-export contract for external importers (library-meta.svelte.ts, library-meta-reducers.ts, publish-flows.svelte.ts, LibraryHome.svelte) keep working.
…ull union geoBasemap, AddMapModal's tileSource, and addMapObject's parameter were all annotated TileSourceDescriptor (the XyzTileSource | DziTileSource union) even though a Map is always the xyz variant — the annotation widened the literal and erased that. geo-notes.ts's geoLabelOf/geoForTarget then failed calling core's pixelToLngLat/lngLatToPixel, which need Extentish (tileSize+maxZoom, meaningless for a DZI pyramid). Narrow all four to XyzTileSource: fixes the typecheck errors and, as a side effect, makes it a type error to ever feed a DZI descriptor into the geo/Web-Mercator math, which was always nonsensical.
Ledger ledgers/ARTIFACTS.md row 1.
…publish-flows, save-queue) - binding.ts: FileSystemWritableFileStream.write's chunk type excludes SharedArrayBuffer-backed views; fflate's zip stream never produces one, so assert Uint8Array<ArrayBuffer> instead of the wrong-generic ArrayBufferView cast. - collab.test.ts: used AnnotationSession's private `log` where every other line in the file already used the public `entries` getter for the same value. - publish-flows.svelte.ts: LogLookup's real signature takes a plain string exhibitId; annotating the three lambdas' parameter as the branded LogicalId narrowed it below what the callback type allows (contravariance violation). - save-queue.svelte.ts: non-null assert the last-message index, already guarded by the preceding length check (existing codebase idiom for noUncheckedIndexedAccess). `pnpm --filter @archie/studio exec tsc --noEmit` is now fully green; all 148 tests still pass.
No generator script for this report exists anywhere in the repo, so a committed copy can only drift the moment code changes. Ledger ledgers/ARTIFACTS.md row 2.
addPendingNotes's dedup key (`${p.objectId}\0${p.comment}`) needs a
separator guaranteed absent from real data. It was written as a raw
0x00 byte pasted into the source text instead of the JS escape
sequence — identical at runtime, but it made grep, ripgrep, git grep,
and the fff tools treat the whole 2147-line file as binary and
silently report zero matches on every search.
Ledger ledgers/ARTIFACTS.md row: NUL byte. Verified: file(1) now
reports text, git grep matches inside the file, studio suite green
(148/148).
The tsconfig.json + all the error fixes landed in prior commits, but the actual "typecheck": "tsc --noEmit" script was never added to package.json — pnpm -r typecheck silently skipped studio the whole time (it only runs the script in packages that declare it). Root `pnpm typecheck` now covers all 6 packages.
Root dist/ hand-mirrors packages/archie-viewer/dist/ so jsDelivr's /gh/ root-relative serving resolves the @v1 embed URL (a656cda). No build step enforced the two trees stay identical. Adds `pnpm sync-dist` (resync) and `pnpm sync-dist:check` (verify, exit 1 on drift), and documents the release rule in ADR-0019 + the README embed recipe. Ledger ledgers/ARTIFACTS.md row: dist/.
Ledger ledgers/ARTIFACTS.md: all four rows done, zero accident rows remain. ISSUES.md Issue 3 -> done 2026-07-05.
Runs on every push and on PRs targeting main. Four independent jobs so each check's pass/fail is visible separately in the PR UI: pnpm typecheck (all 6 packages), pnpm test (per-package vitest via --no-bail), astro check for apps/viewer, and a gh-pages-build verification (separate from deploy.yml's actual deploy build — catches build breaks on PRs, where deploy.yml never runs). Same Node 24 / pnpm 10 / LFS checkout as deploy.yml; deploy.yml itself untouched.
Created during the Issue 1 typecheck repair but never actually staged — the "typecheck" script added to package.json would have had nothing to run against on a fresh checkout/CI runner.
…heckout apps/viewer/tsconfig.json includes .astro/types.d.ts for the astro/client triple-slash reference that gives import.meta.env its type — but .astro/ is gitignored (generated) and only existed in local working dirs that had already run `astro dev`/`build`/`check` at least once. A fresh clone (any CI runner) had nothing to generate it, so `tsc --noEmit` failed on published.ts's import.meta.env access with "Property 'env' does not exist on type 'ImportMeta'" — caught by cloning into /tmp and re-running typecheck there rather than trusting the dirty working copy. Added a `pretypecheck` script (matching the existing predev/prebuild convention in this package) that runs `astro sync` first.
…checks.yml catches it)
micahchoo
added a commit
that referenced
this pull request
Jul 7, 2026
qa/hooks/install.sh points core.hooksPath at qa/hooks, but the four git-lfs hooks (post-checkout/post-commit/post-merge/pre-push) living there were untracked. A fresh clone already gets working LFS hooks auto-installed into .git/hooks by git-lfs itself during checkout -- running install.sh then SILENTLY switches the effective hooksPath away from that working directory to one missing them, with zero warning. Tracking these (stock, repo-agnostic git-lfs boilerplate, confirmed byte-identical to what git-lfs generates) closes the regression: once core.hooksPath points at qa/hooks, the LFS hooks are there too. Ledger: ledgers/COLDSTART.md fix #1.
micahchoo
added a commit
that referenced
this pull request
Jul 19, 2026
Decision Archie-da38: narrative beats stay authored in the object editor (ADR-0005/ADR-0016 §56); the overview spine stays read-only. W11's round-trip friction is fixed by navigation, not a second authoring surface — each spine row now jumps to the editor at the beat's object with the Narrative panel scrolled + highlighted on that section, and browser back returns to the overview for free (an ordinary ADR-0024 place push, no new URL rung). - App.svelte: openBeat(sectionId) — enters the editor and reuses navigateToSection's switchObject+focusSectionId to set the transient focus intent (ADR-0024 #1: selected panels/scroll are never in the URL). - ExhibitOverview.svelte: spine rows call onopenbeat instead of onopenobject; a row whose beat object no longer exists (deleteObjectNotesAndMeta drops the object but doesn't prune orphaned sections) renders inert (ns-beat-gone) instead of linking to a dead target. - NarrativeEditor.svelte: activeSectionId changes now scroll the target card into view, move focus to it (tabindex="-1" + aria-label, so screen readers announce it), and play a one-shot outline pulse (geometry, not just color) layered on the existing persistent .active border.
micahchoo
added a commit
that referenced
this pull request
Jul 19, 2026
…hie-a911)
Durable shape for the section rev-log — the piece the probe explicitly did
not prove (PROBE-structure-revlog sharp edge 5).
- structure-serialize.ts: SectionRecord <-> on-disk JSON, both directions
carry-sentinel guarded (rule #3). Composed SectionKey strips to the LOCAL
id at the boundary (sharp edge 4); sectionKey() recomposes on read and
stays the sole composer (re-asserts containment on untrusted on-disk ids).
- structure-persist.ts: history/{localId}.json pages + history/index.json,
content pages FIRST / index LAST (rule #1 commit point). Per-page tolerant
read (rule #2): corrupt page skips-and-reports (StructureCorruptError
sibling); absent store (no dir / no committed index) reads empty, never
corrupt, and torn pages never read as complete.
- 15 tests: full author->persist(MemoryFilesystem)->reload->project
deep-equal vs never-persisted projection (creates, edits, kept tombstone,
un-delete, resolved conflict, dangling ref); write-order instrument;
torn-index; missing/torn/wrong-schema/hostile-id pages; per-item skip.
Gates: render-core vitest 901 passed (80 files); tsc --noEmit exit 0;
pnpm -r typecheck exit 0.
micahchoo
added a commit
that referenced
this pull request
Jul 19, 2026
…e-aef4)
publishLibrary (and thus libraryToZipFs/libraryToZip and the folder writeTree
path — all sinks share the one projection) now accepts PublishOptions.getStructure
and, for each exhibit whose lookup returns a non-empty section rev-log, writes
{slug}/structure/history/{localId}.json + history/index.json via writeStructure —
the exact layout mergeImportedStructure (Archie-2a9a) reads, unlatching the
zip/folder import merge. Pages-first-index-LAST holds inside the emission (rule #1),
and archie.json stays the LAST write of the whole publish (order test-pinned).
Emission is driven by log EXISTENCE, deliberately NOT by the archie.structureRevlog
flag: a log only exists if the flag was ON while authoring, and a library that HAS
history must carry it regardless of the flag's current position. No log -> no
structure/ dir; the tree is byte-identical to the pre-aef4 output (test-pinned).
Studio wiring: openExhibitStructureDirIfExists (store.ts, non-creating probe — a
publish never grows an empty structure/ dir) + a getStructure closure passed to all
three publish call sites (memory projection / zip / folder), tolerant-read with a
corrupt-page advisory (rule #2).
Proof: two-author end-to-end round trip (A publishes zip with structure -> B with a
concurrent edit imports via openArchieLibrary + replaceProjectFrom -> 2 heads on the
touched section, projected conflicted) + viewer-tolerance pin (published tree with
structure renders identical to one without).
micahchoo
added a commit
that referenced
this pull request
Jul 19, 2026
…ingest blocker (scale/ingest-batch) Every imported object drove one whole-library.json rewrite (JSON.stringify of the entire library, plus a read-first snapshotIfUnparseable) — a 100×100 collection ingest paid ~10,000 sequential full-file rewrites, O(N²) cumulative bytes. This was the #1 blocker for large ingests. library-meta.svelte.ts: - Add appendObjects(slug, objects[]) — the bulk sibling of appendObject, modeled on removeObjects/patchExhibits: fold N appends through appendObjmectIn into ONE meta mutation + ONE awaited persist + ONE `exhibit-assets` dirt. Empty batch is a no-op (identity preserved). No signalLibraryChanged (object-append isn't a library-STRUCTURE change), matching the singular appendObject. ingest-flows.ts: - Add a durable-append batch (beginBatch) with reserveId/add/flushIfFull/flush. Media byte-writes and seedMaster stay per object; only the library.json append coalesces. The view steers to the landed tail only AFTER its chunk is durable, so `current` never points at an object not yet in the reactive meta. reserveId mints ids against a running set (the meta isn't mutated until a flush, so nextObjectId would otherwise repeat). - Convert importManifestObjects (manifest + collection), addFiles, and newExhibitFromFolder to the batch. - Chunking decision: persist every 25 objects, NOT one flush at the very end. A single end flush is O(N) bytes but a crash mid-import loses the whole import; chunking bounds crash-loss to <25 objects (≈ the per-object durability the old code paid O(N²) for) while cutting rewrites 25×. A typical IIIF manifest (≤25 canvases) is thus exactly ONE persist — the collection path's "1 per manifest" target — and only a pathologically large single manifest takes several. Every batch flush is awaited before the flow returns/navigates (durable-before-navigate); progress (setImportStatus) still ticks per object. - Failure containment: newExhibitFromManifest now catches an exhibit-create throw and surfaces it via ctx.alert (the fetch/parse failure channel) — no unhandled rejection. importManifestObjects still lets a flush throw propagate so the collection drain catches it as fatal (unchanged skip/fatal/createdSlugs semantics). Tests: pin persist-count (100-canvas manifest → 4 appendObjects, 0 appendObject; 100-file drop → 4; 60 → [25,25,10]; unique ids across the batch) and the containment behavior; add appendObjects store tests. asset-queue.test.ts's inline IngestContext mock gains appendObjects (mechanical — forced by the new API). Gates: apps/studio pnpm typecheck clean; vitest 658/658 green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements ISSUES.md Issue 1 — CI was deploy-only and root typecheck was red on main.
fixes; render-mount: an
addOverlaytype mismatch plus ~20 strict-mode test errors; studio: a deadduplicate-interface block, an
XyzTileSourcenarrowing cluster, and a handful of one-off fixes —see
ledgers/GATE.mdfor the full per-check inventory and commit trail).apps/studio/tsconfig.json+ a"typecheck"script (studio had none)..github/workflows/checks.yml: four independent jobs (typecheck, test, astro-check,gh-pages-build) on every push + PRs targeting main.
deploy.ymlis untouched./tmpand re-running everything there ratherthan trusting the dirty working copy:
apps/viewer's typecheck depends on a gitignored,astro-generated
.astro/types.d.tsthat only existed locally because dev/build had been run herebefore. Added a
pretypecheck: astro syncstep (matching the existing predev/prebuild convention).Note: this branch also carries several commits from a concurrent session working ISSUES.md Issue 3
(tracked-artifact cleanup — NUL byte,
--output,anti-pattern-report.txt, rootdist/sync) andone Issue 4 inventory commit, all made to local
mainwhile this CI work was in progress. Both wereverified together (full typecheck + test sweep re-run at current HEAD).
Test plan
pnpm typecheckgreen on a genuinely fresh clone (not just the working copy)pnpm testgreen on a fresh clone (688+116+7+98+63+148 = 1120 tests)pnpm --filter @archie/viewer run check(astro check) greenbash scripts/build-gh-pages.shsucceedschecks.ymlruns green on this PR in GitHub Actions